//
// NamedEntityBackend.cs
//
// Copyright (C) 2008 Kevin Godby <godbyk@gmail.com>
//

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

using Dashboard;
using Dashboard.Util;
using Dashboard.Engine;

namespace Dashboard.Engine.Backends
{
	// This is a cluechainer that produces clues
	// by extracting names, locations, dates, and times from
	// textblocks.

	public class NamedEntityBackend : Backend, IComponent
	{
		public void Initialize ()
		{
			// FIXME hardcoded path
			models_path = System.IO.Path.Combine ("/usr/local/lib/opennlp-sharp/", "models");
			// TODO if models_path doesn't exist, bail out.
			// TODO instead of models_path, check the namefind path (below)
			entity_finder = new OpenNLP.Tools.NameFind.EnglishNameFinder(System.IO.Path.Combine (models_path, "namefind"));
			Engine.MessageBroker.Subscribe ("/event/*", typeof (ClueMessage), OnMessage);
		}

		public void Dispose ()
		{

		}

		private void OnMessage (Message message)
		{
			Engine.Log.Debug ("Processing message for named entities...");

			ClueMessage clue_message = message as ClueMessage;

			// Extract named entities from the textblocks and
			// construct new clues based on that.

			bool found_keywords = false;

			foreach (Clue clue in clue_message.Clues) {

				// If the clue already contains the
				// dc:keywords property we've probably
				// already processed the clue.
				// FIXME: This is a bit crude.

				if (clue.Properties.Contains ("dc:keywords"))
					continue;

				// We try to extract keywords from all 
				// properties of type dc:text.

				foreach (Property property in clue.Properties.ToArray ()) {
					if (property.Type != "dc:text" || String.IsNullOrEmpty (property.Value))
						continue;
					
					List<Property> named_entities = ExtractEntities (property.Value);
					
					if (named_entities.Count == 0)
						continue;

					found_keywords = true;
					
					clue.Properties.AddRange (named_entities);
				}
			}
			
			if (found_keywords)
				Engine.MessageBroker.Send (clue_message);
		}
		
		private List<Property> ExtractEntities (string text)
		{
			List<Property> named_entities = new List<Property> ();

			if (entity_finder == null)
				entity_finder = new OpenNLP.Tools.NameFind.EnglishNameFinder(System.IO.Path.Combine (models_path, "namefind"));

			string[] models = new string[] {
				"date", 
				"location", 
				"money", 
				"organization", 
				"percentage", 
				"person", 
				"time"
			};

			string result = entity_finder.GetNames(models, text);

			// Parse out the named entities
			foreach (string model in models) {
				Engine.Log.Debug ("Searching for {0}...", model);
				string re = "<" + model + ">.*?</" + model + ">";
				if (Regex.IsMatch (result, re)) {
					System.Text.RegularExpressions.Match match = Regex.Match(result, re);
					while (match != System.Text.RegularExpressions.Match.Empty) {
						//named_entities.Add (match.ToString ());
						// Strip off the SGML tags
						re = "<" + model + ">|</" + model + ">";
						string named_entity = Regex.Replace(match.ToString (), re, "");
						Engine.Log.Debug ("Found {0}: \"{1}\"", model, named_entity);
						Property property = new Property("db:"+model, "db:string", named_entity);
						named_entities.Add (property);
						match = match.NextMatch ();
					}
				}
			}

			return named_entities;
		}
		
		public string Name
		{
			get { return "NamedEntity"; }
		}

		public string Description
		{
			get { return "Uses OpenNLP# to extract entities (such as person names, dates, times, and locations) from plain text."; }
		}

		private OpenNLP.Tools.NameFind.EnglishNameFinder entity_finder;
		private string models_path;

	}
}
